package org.apache.solr.schema;

import java.util.HashMap;
import java.util.Map;
import org.apache.lucene.search.Query;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.search.QParser;


/**
 * An abstract base class for FieldTypes that delegate work to another {@link org.apache.solr.schema.FieldType}.
 * The sub type can be obtained by either specifying the subFieldType attribute or the subFieldSuffix.  In the former
 * case, a new dynamic field will be injected into the schema automatically with the name of {@link #POLY_FIELD_SEPARATOR}.
 * In the latter case, it will use an existing dynamic field definition to get the type.  See the example schema and the
 * use of the {@link org.apache.solr.schema.PointType} for more details.
 *
 **/
public abstract class AbstractSubTypeFieldType extends FieldType implements SchemaAware {

    protected FieldType subType;
    public static final String SUB_FIELD_SUFFIX = "subFieldSuffix";
    public static final String SUB_FIELD_TYPE = "subFieldType";
    protected String suffix;
    protected int dynFieldProps;
    protected String[] suffixes;
    protected IndexSchema schema;   // needed for retrieving SchemaFields

    public FieldType getSubType() {
        return subType;
    }

    @Override
    protected void init(IndexSchema schema, Map<String, String> args) {

        this.schema = schema;
        //it's not a first class citizen for the IndexSchema
        SolrParams p = new MapSolrParams(args);
        String subFT = p.get(SUB_FIELD_TYPE);

        String subSuffix = p.get(SUB_FIELD_SUFFIX);
        if (subFT != null) {
            args.remove(SUB_FIELD_TYPE);
            subType = schema.getFieldTypeByName(subFT.trim());
            suffix = POLY_FIELD_SEPARATOR + subType.typeName;
        }
        else if (subSuffix != null) {
            args.remove(SUB_FIELD_SUFFIX);
            suffix = subSuffix;
        }
        else {
            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "The field type: " + typeName
                    + " must specify the " + SUB_FIELD_TYPE + " attribute or the " + SUB_FIELD_SUFFIX + " attribute.");
        }
    }

    /**
     * Helper method for creating a dynamic field SchemaField prototype. Returns
     * a {@link SchemaField} with the {@link FieldType} given and a name of "*"
     * + {@link FieldType#POLY_FIELD_SEPARATOR} + {@link FieldType#typeName} and
     * props of indexed=true, stored=false.
     *
     * @param schema the IndexSchema
     * @param type The {@link FieldType} of the prototype.
     * @return The {@link SchemaField}
     */
    static SchemaField registerPolyFieldDynamicPrototype(IndexSchema schema, FieldType type) {

        String name = "*" + FieldType.POLY_FIELD_SEPARATOR + type.typeName;

        Map<String, String> props = new HashMap<>();
        //Just set these, delegate everything else to the field type
        props.put("indexed", "true");
        props.put("stored", "false");
        props.put("multiValued", "false");

        int p = SchemaField.calcProps(name, type, props);

        SchemaField proto = SchemaField.create(name, type, p, null);
        schema.registerDynamicField(proto);
        return proto;
    }

    @Override
    public void inform(IndexSchema schema) {

        //Can't do this until here b/c the Dynamic Fields are not initialized until here.
        if (subType != null) {
            SchemaField proto = registerPolyFieldDynamicPrototype(schema, subType);
            dynFieldProps = proto.getProperties();
        }
    }

    /**
     * Throws UnsupportedOperationException()
     */
    @Override
    public Query getFieldQuery(QParser parser, SchemaField field, String externalVal) {
        throw new UnsupportedOperationException();
    }

    protected void createSuffixCache(int size) {
        suffixes = new String[size];
        for (int i = 0; i < size; i++) {
            suffixes[i] = "_" + i + suffix;
        }
    }

    protected SchemaField subField(SchemaField base, int i) {
        return schema.getField(base.getName() + suffixes[i]);
    }
}
